package com.newx.test.open.controller;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.sign.SaSignUtil;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.crypto.SecureUtil;
import com.newx.open.sign.NewxSignUtil;
import com.newx.open.util.NewxRequest;
import com.newx.test.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author yxl
 * @date 2024/7/25 0025 16:40
 */
@RestController
@RequestMapping("/open")
@Slf4j
public class OpenController {

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 为指定用户添加指定余额
     *
     * @param userId 用户 id
     * @param money 要添加的余额，单位：分
     * @return /
     */
    @RequestMapping("addMoney1")
    public R addMoney1(long userId, long money) {
        // 处理业务
        // ...
       log.info("为用户{},添加余额{}成功", userId, money);
       String msg = "为用户" + userId + "添加余额" + money + "成功";
        // 返回
        return R.ok().message(msg);
    }

    /**
     * 为指定用户添加指定余额
     *
     * @param userId 用户 id
     * @param money 要添加的余额，单位：分
     * @param secretKey 秘钥
     * @return /
     */
    @RequestMapping("addMoney2")
    public R addMoney2(long userId, long money, String secretKey) {
        // 1、先校验 secretKey 参数是否正确，如果不正确直接拒绝响应请求
        if(!secretKey.equals("123456")) {
            return R.error().message("无效 secretKey，无法响应请求");
        }
        // 处理业务
        // ...
        log.info("为用户{},添加余额{}成功", userId, money);
        String msg = "为用户" + userId + "添加余额" + money + "成功";
        // 返回
        return R.ok().message(msg);
    }

    /**
     * 为指定用户添加指定余额
     *
     * @param userId 用户 id
     * @param money 要添加的余额，单位：分
     * @param sign 签名
     * @return /
     */
    @RequestMapping("addMoney3")
    public R addMoney3(long userId, long money,String sign) {

        // 在 B 系统，使用同样的算法、同样的密钥，计算出 sign2，与传入的 sign 进行比对
        String secretKey = "123456";
        String signStr = SecureUtil.md5("money=" + money + "&userId=" + userId + "&key=" + secretKey);
        if(!signStr.equals(sign)) {
            return R.error().message("无效 sign，无法响应请求");
        }
        // 处理业务
        // ...
        log.info("为用户{},添加余额{}成功", userId, money);
        String msg = "为用户" + userId + "添加余额" + money + "成功";
        // 返回
        return R.ok().message(msg);
    }

    /**
     * 为指定用户添加指定余额
     *
     * @param userId 用户 id
     * @param money 要添加的余额，单位：分
     * @param nonce 随机数
     * @param sign 签名
     * @return /
     */
    @RequestMapping("addMoney4")
    public R addMoney4(long userId, long money, String nonce, String sign) {
        String cacheNonce = redisTemplate.opsForValue().get("nonce_" + nonce);
        // 1、检查此 nonce 是否已被使用过了
        if(cacheNonce != null) {
            return R.error().message("此 nonce 已被使用过了，请求无效");
        }

        // 在 B 系统，使用同样的算法、同样的密钥，计算出 sign2，与传入的 sign 进行比对
        String secretKey = "123456";
        String signStr = SecureUtil.md5("money=" + money + "&nonce=" + nonce + "&userId=" + userId + "&key=" + secretKey);
        if(!signStr.equals(sign)) {
            return R.error().message("无效 sign，无法响应请求");
        }
        // 3、将 nonce 记入缓存，防止重复使用
        redisTemplate.opsForValue().set("nonce_" + nonce, "1");
        // 处理业务
        // ...
        log.info("为用户{},添加余额{}成功", userId, money);
        String msg = "为用户" + userId + "添加余额" + money + "成功";
        // 返回
        return R.ok().message(msg);
    }

    /**
     * 为指定用户添加指定余额
     *
     * @param userId 用户 id
     * @param money 要添加的余额，单位：分
     * @param nonce 随机数
     * @param timestamp 时间戳
     * @param sign 签名
     * @return /
     */
    @RequestMapping("addMoney5")
    public R addMoney5(long userId, long money, String nonce, Long timestamp, String sign) {
        // 1、检查 timestamp 是否超出允许的范围（此处假定最大允许2分钟差距）
        long timestampDisparity = System.currentTimeMillis() - timestamp; // 实际的时间差
        if(timestampDisparity > 1000 * 60 * 2) {// 2分钟
            return R.error().message("timestamp 时间差超出允许的范围，请求无效");
        }

        String cacheNonce = redisTemplate.opsForValue().get("nonce_" + nonce);
        // 2、检查此 nonce 是否已被使用过了
        if(cacheNonce != null) {
            return R.error().message("此 nonce 已被使用过了，请求无效");
        }

        // 3、在 B 系统，使用同样的算法、同样的密钥，计算出 sign2，与传入的 sign 进行比对
        String secretKey = "123456";
        String signStr = SecureUtil.md5("money=" + money + "&nonce=" + nonce + "&timestamp=" + timestamp + "&userId=" + userId + "&key=" + secretKey);
        if(!signStr.equals(sign)) {
            return R.error().message("无效 sign，无法响应请求");
        }
        // 4、将 nonce 记入缓存，ttl 有效期和 allowDisparity 允许时间差一致
        redisTemplate.opsForValue().set("nonce_" + nonce, "1", 2, TimeUnit.MINUTES);
        // 处理业务
        // ...
        log.info("为用户{},添加余额{}成功", userId, money);
        String msg = "为用户" + userId + "添加余额" + money + "成功";
        // 返回
        return R.ok().message(msg);
    }

    /**
     * 为指定用户添加指定余额
     *
     * @param userId 用户 id
     * @param money 要添加的余额，单位：分
     * @param nonce 随机数
     * @param timestamp 时间戳
     * @param sign 签名
     * @return /
     */
    @RequestMapping("addMoney6")
    public R addMoney6(long userId, long money, String nonce, Long timestamp, String sign) {
        // 1、检查 timestamp 是否超出允许的范围（此处假定最大允许2分钟差距）（重点一：此处需要取绝对值）
        long timestampDisparity = Math.abs(System.currentTimeMillis() - timestamp); // 实际的时间差
        if(timestampDisparity > 1000 * 60 * 2) {// 2分钟
            return R.error().message("timestamp 时间差超出允许的范围，请求无效");
        }

        String cacheNonce = redisTemplate.opsForValue().get("nonce_" + nonce);
        // 2、检查此 nonce 是否已被使用过了
        if(cacheNonce != null) {
            return R.error().message("此 nonce 已被使用过了，请求无效");
        }

        // 3、在 B 系统，使用同样的算法、同样的密钥，计算出 sign2，与传入的 sign 进行比对
        String secretKey = "123456";
        String signStr = SecureUtil.md5("money=" + money + "&nonce=" + nonce + "&timestamp=" + timestamp + "&userId=" + userId + "&key=" + secretKey);
        if(!signStr.equals(sign)) {
            return R.error().message("无效 sign，无法响应请求");
        }
        // 4、将 nonce 记入缓存，防止重复使用，ttl 有效期和 allowDisparity 允许时间差一致（重点二：此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ）
        redisTemplate.opsForValue().set("nonce_" + nonce, "1", 2*2, TimeUnit.MINUTES);
        // 处理业务
        // ...
        log.info("为用户{},添加余额{}成功", userId, money);
        String msg = "为用户" + userId + "添加余额" + money + "成功";
        // 返回
        return R.ok().message(msg);
    }

    // 为指定用户添加指定余额
    @RequestMapping("addMoney7")
    public SaResult addMoney7(long userId, long money) {

        // 1、校验请求中的签名
        SaSignUtil.checkRequest(SaHolder.getRequest());
        // 2、校验通过，处理业务
        System.out.println("userId=" + userId);
        System.out.println("money=" + money);

        // 3、返回
        return SaResult.ok();
    }

    @RequestMapping("addMoney8")
    public R addMoney8(HttpServletRequest request) {
        String test = redisTemplate.opsForValue().get("test");
        Map<String, Object> parameterMap = NewxRequest.getParameterMap(request);
        for (String s : parameterMap.keySet()) {
            System.out.println(s + "=" + parameterMap.get(s));
        }
        String appSecret = "123456";
        NewxSignUtil.checkRequest(request,appSecret);
        return R.ok().put("parameterMap",parameterMap);
    }

    /**
     * appId=15236278456&nonce=b3cabd5c94ed46b8b8c8f3de7427a42b&requestData={"userId": "10001", "money":"1000"}&timestamp=1723534451322&key=123456
     * @param request
     * @return
     */
    @RequestMapping("getSign")
    public R getSign(HttpServletRequest request) {
        String appSecret = "123456";
        Map<String, Object> parameterMap = NewxRequest.getParameterMap(request);
        String sign = NewxSignUtil.getSign(parameterMap, appSecret);
        return R.ok().put("sign",sign);
    }

    @RequestMapping("getTimestamp")
    public R getTimestamp() {
        Map<String,Object> map = new HashMap<>();
        return R.ok().put("timestamp",System.currentTimeMillis());
    }

}
